package org.common.utils.database.access;

import com.esotericsoftware.reflectasm.MethodAccess;
import org.common.utils.annotation.Ignore;
import org.common.utils.tool.ClassTool;
import org.common.utils.tool.StringTool;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * sql模板
 * @author zhouzhibing
 * @date 2025/1/8
 */
class SqlTemplate {
    /**
     * 空参数
     */
    private static final Class<?> [] EMPTY_PARAMETERS = new Class[0];
    /**
     * sql模板缓存
     */
    private static final ConcurrentHashMap<Class<?> , SqlTemplate> SQL_TEMPLATE_MAP = new ConcurrentHashMap<>();
    /**
     * 表名
     */
    private final String tableName;
    /**
     * 插入sql模板
     */
    private String insertSqlTemplate;
    /**
     * 删除sql模板
     */
    private String deleteSqlTemplate;
    /**
     * 更新sql模板
     */
    private String updateSqlTemplate;
    /**
     * 查询sql模板
     */
    private String selectSqlTemplate;
    /**
     * 当前类
     */
    private final Class<?> clzss;
    /**
     * 列名列表
     */
    private List<String> columnNameList;
    /**
     * get方法索引列表
     */
    private List<Integer> getMethodIndexList;
    /**
     * set方法索引列表
     */
    private List<Integer> setMethodIndexList;
    /**
     * 函数访问对象
     */
    private final MethodAccess methodAccess;

    /**
     * sql模板构造
     * @param clzss 当前类
     */
    public SqlTemplate(Class<?> clzss) {
        this.clzss = clzss;
        this.tableName = StringTool.toSnakeString(clzss.getSimpleName());
        this.methodAccess = MethodAccess.get(clzss);

        init();
        initInsertSqlTemplate();
        initUpdateSqlTemplate();
        initDeleteSqlTemplate();
        initSelectSqlTemplate();
    }

    /**
     * 初始化模板（列名列表、set方法索引列表、get方法索引列表）
     */
    private void init() {
        List<Field> fields = ClassTool.getAllFields(clzss);
        int fieldLength = fields.size();
        if(fieldLength == 0) {
            throw new IllegalArgumentException(clzss.getName() + " , field empty .");
        }

        this.columnNameList = new ArrayList<>(fieldLength);
        this.setMethodIndexList = new ArrayList<>(fieldLength);
        this.getMethodIndexList = new ArrayList<>(fieldLength);

        for (Field field : fields) {
            //标记该字段忽略
            Ignore ignore = field.getAnnotation(Ignore.class);
            if(ignore != null) {
                continue;
            }

            String fieldName = field.getName();
            String maxHumpString = StringTool.toMaxHumpString(fieldName);
            String setMethodName = "set" + maxHumpString;
            String getMethodName = "get" + maxHumpString;

            //未提供setter方法
            Method setMethod = ClassTool.getMethod(clzss, setMethodName, field.getType());
            if(setMethod == null) {
                continue;
            }

            //记录列名
            String columnName = StringTool.toSnakeString(fieldName);
            this.columnNameList.add(columnName);

            //记录setter, getter方法索引
            int setIndex = methodAccess.getIndex(setMethodName, field.getType());
            int getIndex = methodAccess.getIndex(getMethodName, EMPTY_PARAMETERS);
            this.setMethodIndexList.add(setIndex);
            this.getMethodIndexList.add(getIndex);
        }
    }

    /**
     * 初始化插入sql模板
     */
    private void initInsertSqlTemplate() {
        int size = columnNameList.size();
        StringBuilder stringBuilder = new StringBuilder("insert into ");
        stringBuilder.append("`").append(tableName).append("`(");

        StringBuilder columnString = new StringBuilder();
        StringBuilder valuesString = new StringBuilder();

        for (int i = 0; i < size; i++) {
            String columnName = columnNameList.get(i);
            StringTool.byIndexString(columnString , "`" , columnName , "`" ,  "," , i , size - 1);
            StringTool.byIndexString(valuesString , "?" , "," , i , size - 1);
        }
        stringBuilder.append(columnString).append(") values (").append(valuesString).append(")");
        this.insertSqlTemplate = stringBuilder.toString();
    }

    /**
     * 初始化查询sql模板
     */
    private void initSelectSqlTemplate() {
        int size = columnNameList.size();
        StringBuilder stringBuilder = new StringBuilder("select ");
        for (int i = 0; i < size; i++) {
            String columnName = columnNameList.get(i);
            StringTool.byIndexString(stringBuilder , "`" , columnName , "`" ,  "," , i , size - 1);
        }
        stringBuilder.append(" from `").append(tableName).append("` ");
        this.selectSqlTemplate = stringBuilder.toString();
    }

    /**
     * 初始化删除sql模板
     */
    private void initDeleteSqlTemplate() {
        String columnName = columnNameList.getFirst();
        this.deleteSqlTemplate = "delete from " + "`" + tableName + "` where `" + columnName + "` = ?";
    }

    /**
     * 初始化更新sql模板
     */
    private void initUpdateSqlTemplate() {
        int size = columnNameList.size();
        StringBuilder stringBuilder = new StringBuilder("update ");
        stringBuilder.append("`").append(tableName).append("` set ");
        for (int i = 0; i < size; i++) {
            String columnName = columnNameList.get(i);
            StringTool.byIndexString(stringBuilder , "`" , columnName , "` = ?" ,  "," , i , size - 1);
        }
        stringBuilder.append(" where `").append(columnNameList.getFirst()) .append("` = ?");
        this.updateSqlTemplate = stringBuilder.toString();
    }

    /**
     * 插入参数设置
     * @param statement 预编译
     * @param instance 当前实例对象
     */
    void insertStatementParameter(PreparedStatement statement , Object instance) {
        try {
            for (int i = 0; i < getMethodIndexList.size(); i++) {
                Integer getIndex = getMethodIndexList.get(i);
                Object value = methodAccess.invoke(instance , getIndex);
                statement.setObject(i + 1 , value);
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 更新参数设置
     * @param statement 预编译
     * @param instance 当前实例对象
     */
    void updateStatementParameter(PreparedStatement statement , Object instance) {
        try {
            int statementIndex = 1;
            for (Integer getIndex : getMethodIndexList) {
                Object value = methodAccess.invoke(instance, getIndex);
                statement.setObject(statementIndex++, value);
            }

            Integer getIndex = getMethodIndexList.getFirst();
            Object value = methodAccess.invoke(instance , getIndex);
            statement.setObject(statementIndex , value);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 删除参数设置
     * @param statement 预编译
     * @param instance 当前实例对象
     */
    void deleteStatementParameter(PreparedStatement statement , Object instance) {
        try {
            Integer getIndex = getMethodIndexList.getFirst();
            Object value = methodAccess.invoke(instance , getIndex);
            statement.setObject(1 , value);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 实例化对象
     * @param resultSet 结果集
     * @return 实例对象
     * @param <T> 泛型
     */
    <T> T newInstance(ResultSet resultSet) throws Exception {
        Object instance = clzss.getConstructor().newInstance();
        for (int i = 0; i < setMethodIndexList.size(); i++) {
            Integer setIndex = setMethodIndexList.get(i);
            Object value = resultSet.getObject(i + 1);
            if(value != null)
                methodAccess.invoke(instance , setIndex , value);
        }
        return (T)instance;
    }

    /**
     * 获取sql模板
     * @param clzss 指定类型
     * @return sql模板
     */
    public static SqlTemplate getSqlTemplate(Class<?> clzss) {
        return SQL_TEMPLATE_MAP.computeIfAbsent(clzss, k -> new SqlTemplate(clzss));
    }

    /**
     * @return 表名
     */
    public String getTableName() {
        return tableName;
    }

    /**
     * @return 插入sql模板
     */
    public String getInsertSqlTemplate() {
        return insertSqlTemplate;
    }

    /**
     * @return 删除sql模板
     */
    public String getDeleteSqlTemplate() {
        return deleteSqlTemplate;
    }

    /**
     * @return 更新sql模板
     */
    public String getUpdateSqlTemplate() {
        return updateSqlTemplate;
    }

    /**
     * @return 查询sql模板
     */
    public String getSelectSqlTemplate() {
        return selectSqlTemplate;
    }
}
